package pa4.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;

import pa4.interfaces.Node;
import pa4.interfaces.Peer;

public class NodeImpl<V> extends UnicastRemoteObject implements Node<V> {

	private int id;
	private HashMap<Integer, V> data = new HashMap<Integer, V>();
	private Node<V> leftChild;
	private Node<V> rightChild;
	private String leftChildAddress;
	private String rightChildAddress;
	private PeerImpl<V> peer;
	private int level;
	private boolean isLeaf = false;
	
	protected NodeImpl(int id, int level, PeerImpl<V> peer) throws RemoteException {
		super();
		this.id = id;
		this.level = level;
		this.peer = peer;
		if(level == 6){
			isLeaf = true;
		}
		System.out.println("Node - " + id + " @ Peer " + peer.getName() + " Level " + level);
	}

	@Override
	public Node<V> getLeftChild() throws RemoteException {
		return leftChild;
	}

	@Override
	public Node<V> getRightChild() throws RemoteException {
		return rightChild;
	}

	@Override
	public V get(int key) throws RemoteException {
		//inform the peer that a message has been received
		peer.messageReceived();
		//if the node is a leaf node, retrieve the data
		if(isLeaf){
			return data.get(key);
		}
		//compare the id of the node with the key
		else if(key < id){
			return leftChild.get(key);
		}
		else
			return rightChild.get(key);
	}

	@Override
	public void put(int key, V value) throws RemoteException {
		//inform the peer that a message has been received
		peer.messageReceived();
		//if the node is a leaf node, store the data
		if(isLeaf){
			data.put(key, value);
			peer.valueStored();
		}
		//compare the id of the node with the key
		else if(key < id){
			leftChild.put(key, value);
		}
		else
			rightChild.put(key, value);
		
	}

	@Override
	public void remove(int key) throws RemoteException {
		//inform the peer that a message has been received
		peer.messageReceived();
		//if the node is a leaf node, remove the data
		if(isLeaf){
			data.remove(key);
		}
		//compare the id of the node with the key
		else if(key < id){
			leftChild.remove(key);
		}
		else
			rightChild.remove(key);		
	}

	@Override
	public void setLeftChild(Node<V> child) throws RemoteException {
		leftChild = child;
		leftChildAddress = child.getPeer().getName();
	}

	@Override
	public void setRightChild(Node<V> child) throws RemoteException {
		rightChild = child;
		rightChildAddress = child.getPeer().getName();
	}

	@Override
	public void repair() throws RemoteException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean isLeaf() throws RemoteException {
		return isLeaf;
	}

	@Override
	public String getLeftChildAddress() throws RemoteException{
		return leftChildAddress;
	}

	public void setLeftChildAddress(String leftChildAddress) {
		this.leftChildAddress = leftChildAddress;
	}

	@Override
	public Peer<V> getPeer() throws RemoteException {
		return peer;
	}

	@Override
	public String getRightChildAddress() throws RemoteException {
		return rightChildAddress;
	}

	@Override
	public int getId() throws RemoteException {
		return id;
	}

	@Override
	public int getLeftChildId() throws RemoteException {
		int numerator = (int) Math.ceil((Math.pow(2, level) * id / Integer.MAX_VALUE));
		int leftChildId = (int) ((2 * numerator - 1) * ((Integer.MAX_VALUE)/(Math.pow(2, level + 1))));
		return leftChildId;
	}

	@Override
	public int getRightChildId() throws RemoteException {
		int numerator = (int) Math.ceil((Math.pow(2, level) * id / Integer.MAX_VALUE));
		int rightChildId = (int) ((2 * numerator + 1) * ((Integer.MAX_VALUE)/(Math.pow(2, level + 1))));
		return rightChildId;
	}

	@Override
	public int getLevel() throws RemoteException {
		return level;
	}

}
